# -*- coding: utf-8 -*-
"""
ffpyplayer视频播放器包装器 - 自包含版（不依赖系统FFmpeg）
"""

import os
import time
import numpy as np
import logging
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPainter
from PyQt5.QtWidgets import QWidget
from ffpyplayer.player import MediaPlayer
from ffpyplayer.tools import set_loglevel
from utils.helper import logger

# 设置ffpyplayer日志级别
set_loglevel('warning')


class ScreenRecorder(QThread):
    """
    基于OpenCV的屏幕录制器（不依赖系统FFmpeg，使用QThread实现线程安全）
    参数说明：
        output_path: 录制文件保存路径
        fps: 帧率(默认15，平衡性能和文件大小)
    """
    # 信号：录制完成（返回文件路径，空串表示失败）
    finished = pyqtSignal(str)
    # 信号：错误信息
    error = pyqtSignal(str)
    # 信号：录制进度（已录制秒数）
    progress = pyqtSignal(int)
    
    def __init__(self, output_path, fps=15):
        super().__init__()
        self.output_path = output_path
        self.fps = fps
        self.running = False
        self.start_time = None
        self.writer = None
        self.frame_size = None
        
        # 确保输出目录存在
        output_dir = os.path.dirname(self.output_path)
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir)
    
    def run(self):
        """录屏主逻辑"""
        try:
            # 延迟导入，避免启动时加载过多依赖
            import pyautogui
            import cv2
            from PIL import ImageGrab
            
            self.running = True
            self.start_time = time.time()
            
            # 获取屏幕分辨率
            screen_size = pyautogui.size()
            self.frame_size = (screen_size[0], screen_size[1])
            
            logger.info(f"开始录屏：路径={self.output_path}，分辨率={self.frame_size}，帧率={self.fps}")
            
            # 初始化视频写入器（使用OpenCV内置编码器）
            fourcc = cv2.VideoWriter_fourcc(*"mp4v")  # 使用MP4V编码器
            self.writer = cv2.VideoWriter(
                self.output_path,
                fourcc,
                self.fps,
                self.frame_size
            )
            
            if not self.writer.isOpened():
                raise RuntimeError(f"无法创建视频文件：{self.output_path}")
            
            # 录屏循环
            while self.running:
                # 更新进度
                elapsed = int(time.time() - self.start_time)
                self.progress.emit(elapsed)
                
                # 捕获屏幕图像
                img = ImageGrab.grab(all_screens=True)  # 捕获所有屏幕
                frame = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)  # 转换为OpenCV格式
                
                # 调整尺寸以匹配视频写入器（如果需要）
                if frame.shape[1] != self.frame_size[0] or frame.shape[0] != self.frame_size[1]:
                    frame = cv2.resize(frame, self.frame_size)
                
                # 写入视频帧
                self.writer.write(frame)
                
                # 控制采集频率，避免CPU占用过高
                time.sleep(1 / self.fps)
            
            # 录制完成，检查文件有效性
            if os.path.exists(self.output_path) and os.path.getsize(self.output_path) > 1024:
                logger.info(f"录屏完成：文件大小={os.path.getsize(self.output_path)//1024}KB")
                self.finished.emit(self.output_path)
            else:
                err_msg = "录屏文件无效或过小"
                logger.error(err_msg)
                self.error.emit(err_msg)
                self.finished.emit("")
                
        except Exception as e:
            err_msg = f"录屏异常：{str(e)}"
            logger.error(err_msg)
            self.error.emit(err_msg)
            # 清理无效文件
            if os.path.exists(self.output_path):
                try:
                    os.remove(self.output_path)
                except:
                    pass
            self.finished.emit("")
        finally:
            self._cleanup()
    
    def stop(self):
        """安全停止录制"""
        self.running = False
        # 等待线程结束
        if self.wait(2000):
            logger.info("录屏线程安全停止")
        else:
            logger.warning("录屏线程等待超时")
            self.terminate()
    
    def _cleanup(self):
        """清理资源"""
        if self.writer:
            try:
                self.writer.release()
            except:
                pass
            self.writer = None


class FFVideoWidget(QWidget):
    """视频显示部件"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.image = None
        self.setMinimumSize(640, 480)
        self.setStyleSheet("background-color: black;")

    def set_image(self, image):
        self.image = image
        self.update()

    def paintEvent(self, event):
        if self.image is None:
            return

        painter = QPainter(self)
        rect = self.rect()
        image_width = self.image.width()
        image_height = self.image.height()

        scale = min(rect.width() / image_width, rect.height() / image_height)
        new_width = int(image_width * scale)
        new_height = int(image_height * scale)

        x = (rect.width() - new_width) // 2
        y = (rect.height() - new_height) // 2
        painter.drawImage(x, y, self.image.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation))


class FFVideoPlayer(QThread):
    """基于ffpyplayer的视频播放器（不依赖系统FFmpeg）"""
    frame_updated = pyqtSignal(QImage)
    state_changed = pyqtSignal(str)  # 'playing', 'paused', 'stopped', 'error'
    position_updated = pyqtSignal(int)
    duration_updated = pyqtSignal(int)
    finished = pyqtSignal()

    def __init__(self, video_path):
        super().__init__()
        self.video_path = video_path
        self.player = None
        self.running = False
        self.paused = False
        self.current_position = 0
        self.total_duration = 0
        self.position_update_counter = 0
        self.position_update_interval = 50  # 每50帧更新一次位置（约1秒）

    def run(self):
        try:
            # 1. 验证文件
            if not self._validate_file():
                self._emit_error("视频文件验证失败")
                return

            # 2. 创建播放器（使用ffpyplayer，不依赖系统FFmpeg）
            self.player = MediaPlayer(
                self.video_path,
                ff_opts={
                    'autoexit': True,
                    'sn': True,  # 解决某些MP4格式问题
                    'max_delay': 500000,  # 增加最大延迟
                    'framedrop': 'no',
                    'infbuf': True,  # 无限缓冲区
                    'buffer_size': 2048 * 1024 * 10,  # 更大的缓冲区
                    'f': 'mp4',  # 强制指定格式为mp4
                    'probesize': '10000000',  # 增大探测数据大小
                    'analyzeduration': '20000000',  # 延长分析时长
                    'fpsprobesize': '100'  # 添加帧率探测参数
                }
            )

            if not self.player:
                self._emit_error("播放器创建失败")
                return

            # 3. 获取播放器元数据
            metadata = self.player.get_metadata()
            logger.info(f"播放器元数据: {metadata}")

            # 4. 从播放器获取时长
            self._update_duration_from_metadata(metadata)

            # 5. 检查视频流
            if not self._has_video_stream(metadata):
                self._emit_error("未检测到视频流")
                return

            # 6. 开始播放
            self.running = True
            self.player.set_pause(False)
            self.state_changed.emit('playing')

            # 7. 播放循环
            self._playback_loop()

        except Exception as e:
            logger.error(f"播放器主循环异常: {str(e)}", exc_info=True)
            self._emit_error("播放异常")
        finally:
            self._cleanup()

    def _validate_file(self):
        """验证文件有效性"""
        if not os.path.exists(self.video_path):
            logger.error(f"文件不存在: {self.video_path}")
            return False

        if not os.access(self.video_path, os.R_OK):
            logger.error(f"无读取权限: {self.video_path}")
            return False

        file_size = os.path.getsize(self.video_path)
        logger.info(f"文件大小: {file_size}字节")

        if file_size < 1024:
            logger.error("文件过小，可能无效")
            return False

        return True

    def _update_duration_from_metadata(self, metadata):
        """从播放器元数据更新时长"""
        if 'duration' in metadata and metadata['duration'] is not None:
            try:
                self.total_duration = int(float(metadata['duration']) * 1000)
                self.duration_updated.emit(self.total_duration)
                logger.info(f"播放器提供的时长: {self.total_duration}毫秒")
            except (ValueError, TypeError) as e:
                logger.error(f"解析播放器时长失败: {str(e)}")
                self.total_duration = 0
        else:
            logger.warning("播放器未提供时长信息")

    def _has_video_stream(self, metadata):
        """检查是否有视频流"""
        # 方法1: 检查元数据中的视频流标志
        if 'video' in metadata.get('streams', {}):
            return True

        # 方法2: 检查视频尺寸
        src_vid_size = metadata.get('src_vid_size', (0, 0))
        if src_vid_size[0] > 0 and src_vid_size[1] > 0:
            return True

        # 方法3: 检查帧率
        frame_rate = metadata.get('frame_rate', (0, 0))
        if frame_rate[0] > 0 and frame_rate[1] > 0:
            return True

        # 方法4: 检查像素格式
        if metadata.get('src_pix_fmt', '') != '':
            return True

        logger.warning("所有视频流检查方法均失败")
        return False

    def _playback_loop(self):
        """主播放循环"""
        frame_counter = 0
        consecutive_failures = 0
        max_failures = 50
        eof_count = 0

        while self.running:
            try:
                # 获取帧
                frame, val = self.player.get_frame(show=False, timeout=0.1)  # 增加超时
                logger.debug(f"帧状态: val={val}, frame={frame is not None}")

                # 处理EOF
                if val == 'eof':
                    eof_count += 1
                    logger.info(f"EOF #{eof_count}")
                    if eof_count > 3:
                        logger.info("多次EOF，结束播放")
                        break
                    self.player.seek(0, relative=False)  # 重置到开头
                    time.sleep(0.1)
                    continue
                else:
                    eof_count = 0

                # 处理有效帧
                if frame is not None:
                    img, t = frame
                    if img is not None:
                        try:
                            q_image = self._convert_frame(img)
                            if q_image:
                                self.frame_updated.emit(q_image)
                                frame_counter += 1
                                consecutive_failures = 0  # 重置失败计数

                                # 每10帧记录一次
                                if frame_counter % 10 == 0:
                                    logger.debug(f"已处理帧数: {frame_counter}")
                        except Exception as e:
                            logger.error(f"帧转换失败: {str(e)}")
                            consecutive_failures += 1
                    else:
                        consecutive_failures += 1
                        logger.debug(f"图像为空，失败计数: {consecutive_failures}")
                else:
                    consecutive_failures += 1
                    logger.debug(f"帧为空，失败计数: {consecutive_failures}")

                # 检查连续失败
                if consecutive_failures >= max_failures:
                    logger.warning(f"连续{consecutive_failures}次失败，尝试重建播放器")
                    if not self._rebuild_player():
                        logger.error("重建播放器失败，停止播放")
                        break
                    consecutive_failures = 0

                # 更新位置
                self._update_position()

                # 短暂休眠
                time.sleep(0.005)

            except Exception as e:
                logger.error(f"播放循环异常: {str(e)}")
                consecutive_failures += 1
                if consecutive_failures >= max_failures:
                    break

        # 播放结束
        logger.info(f"播放结束，总帧数: {frame_counter}")
        self.state_changed.emit('stopped')
        self.finished.emit()

    def _convert_frame(self, img):
        """转换帧为QImage"""
        try:
            # 获取图像信息
            w, h = img.get_size()
            fmt = img.get_pixel_format()

            # 获取图像数据
            if hasattr(img, 'to_bytearray'):
                data = img.to_bytearray()[0]
            elif hasattr(img, 'to_memoryview'):
                data = img.to_memoryview()[0].tobytes()
            else:
                logger.error("无法获取图像数据")
                return None

            # 根据格式创建QImage
            if fmt == 'rgb24':
                return QImage(data, w, h, 3 * w, QImage.Format_RGB888)
            elif fmt == 'rgba':
                return QImage(data, w, h, 4 * w, QImage.Format_RGBA8888)
            elif fmt == 'yuv420p':
                # 注意：需要转换为RGB
                logger.warning("yuv420p格式需要转换，暂不支持")
                return None
            else:
                logger.warning(f"不支持的像素格式: {fmt}")
                return None
        except Exception as e:
            logger.error(f"转换帧异常: {str(e)}")
            return None

    def _update_position(self):
        """更新播放位置"""
        self.position_update_counter += 1
        if self.position_update_counter >= self.position_update_interval:
            try:
                if self.player:
                    pts = self.player.get_pts()
                    if pts is not None:
                        self.current_position = int(pts * 1000)
                        self.position_updated.emit(self.current_position)
            except Exception as e:
                logger.error(f"更新位置异常: {str(e)}")
            finally:
                self.position_update_counter = 0

    def _rebuild_player(self):
        """重建播放器"""
        try:
            logger.info("重建播放器...")
            self.player.close_player()
            self.player = MediaPlayer(
                self.video_path,
                ff_opts={
                    'autoexit': True,
                    'sn': True,
                    'max_delay': 500000,
                    'framedrop': 'no',
                    'infbuf': True,
                    'buffer_size': 2048 * 1024 * 10,
                    'f': 'mp4'
                }
            )
            if self.player:
                self.player.set_pause(False)
                logger.info("重建成功")
                return True
            return False
        except Exception as e:
            logger.error(f"重建播放器失败: {str(e)}")
            return False

    def _emit_error(self, message):
        """发送错误信号"""
        logger.error(message)
        self.state_changed.emit('error')
        self.finished.emit()

    def _cleanup(self):
        """清理资源"""
        self.running = False
        if self.player:
            try:
                self.player.close_player()
            except:
                pass
            self.player = None

    # 公共方法
    def play(self):
        if self.player and not self.paused:
            self.player.set_pause(False)
            self.paused = False
            self.state_changed.emit('playing')

    def pause(self):
        if self.player:
            self.player.set_pause(True)
            self.paused = True
            self.state_changed.emit('paused')

    def stop(self):
        self.running = False

    def set_position(self, position_ms):
        try:
            if self.player and self.total_duration > 0:
                position_ratio = position_ms / self.total_duration
                self.player.seek(position_ratio)
                self.current_position = position_ms
                self.position_updated.emit(self.current_position)
        except Exception as e:
            logger.error(f"设置位置失败: {str(e)}")

    def is_playing(self):
        return self.running and not self.paused

    def get_position(self):
        return self.current_position

    def get_duration(self):
        return self.total_duration

    def stop_thread(self):
        self.running = False